﻿<?xml version="1.0" encoding="utf-8"?>
<!-- 
    Spun AJAX Framework
    Copyright (C) 2008 Blake Hegerle

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-->
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns="http://www.w3.org/1999/xhtml"
                xmlns:html="http://www.w3.org/1999/xhtml"
                xmlns:view="http://spun.googlecode.com/svn/tags/2008-02-01/Views.xsd"
                exclude-result-prefixes="html view">

    <xsl:output method="xml"
                indent="yes"
                version="1.0"
                encoding="utf-8"
                omit-xml-declaration="no"
                doctype-public="-//W3C//DTD XHTML 1.1//EN"
                doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"/>

    <xsl:template name="Error">
        <xsl:param name="Message" />

        <xsl:message terminate="yes">
            <xsl:value-of select="$Message"/>
        </xsl:message>
    </xsl:template>

    <xsl:template match="html:html">
        <html>
            <head>

                <script type="text/javascript" src="Content/prototype-1.6.0.2.js" />
                <script type="text/javascript" src="Content/Spun-1.0.0.js" />

                <xsl:apply-templates select="html:head" />
            </head>

            <xsl:apply-templates select="html:body" />
        </html>
    </xsl:template>

    <xsl:template match="html:body">
        <body>
            <xsl:for-each select="@*">
                <xsl:copy />
            </xsl:for-each>

            <xsl:apply-templates select="*" />
            <script type="text/javascript">
                <xsl:apply-templates select="*" mode="Bind" />

                <xsl:text>StartSpun();
                </xsl:text>
            </script>
        </body>
    </xsl:template>

    <xsl:template match="*">
        <xsl:element name="{local-name(.)}">
            <xsl:for-each select="@*">
                <xsl:copy />
            </xsl:for-each>

            <xsl:apply-templates select="node()" />
        </xsl:element>
    </xsl:template>

    <xsl:template match="view:inline">
        <xsl:variable name="ID" select="generate-id(.)" />

        <xsl:if test="@path">
            <span id="{$ID}" />
        </xsl:if>
    </xsl:template>

    <xsl:template match="view:checkbox">
        <xsl:variable name="ID" select="generate-id(.)" />

        <xsl:if test="@path">
            <input type="checkbox" id="{$ID}" />
        </xsl:if>

        <xsl:if test="@label">
            <label for="{$ID}">
                <xsl:value-of select="@label"/>
            </label>
        </xsl:if>
    </xsl:template>

    <xsl:template match="view:text">
        <xsl:variable name="ID" select="generate-id(.)" />

        <xsl:if test="@path">
            <input id="{$ID}" />
        </xsl:if>
    </xsl:template>

    <xsl:template match="view:int">
        <xsl:variable name="ID" select="generate-id(.)" />

        <xsl:if test="@path">
            <input id="{$ID}" />
        </xsl:if>
    </xsl:template>

    <xsl:template match="view:float">
        <xsl:variable name="ID" select="generate-id(.)" />

        <xsl:if test="@path">
            <input id="{$ID}" />
        </xsl:if>
    </xsl:template>

    <xsl:template match="view:sequence">
        <xsl:variable name="Id" select="generate-id(.)" />

        <xsl:choose>
            <xsl:when test="count(*) = 1">
                <xsl:for-each select="*">
                    <xsl:copy>
                        <xsl:for-each select="@*">
                            <xsl:copy />
                        </xsl:for-each>

                        <xsl:attribute name="id">
                            <xsl:value-of select="$Id"/>
                        </xsl:attribute>
                    </xsl:copy>
                </xsl:for-each>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="Error">
                    <xsl:with-param name="Message">
                        A view:sequence must have exactly one child element.
                    </xsl:with-param>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="view:button">
        <button>
            <xsl:attribute name="onclick">
                <xsl:text>Action('</xsl:text>
                <xsl:value-of select="@controller" />
                <xsl:text>', '</xsl:text>
                <xsl:value-of select="@action" />
                <xsl:text>');</xsl:text>
            </xsl:attribute>

            <xsl:apply-templates select="child::node()" />
        </button>
    </xsl:template>

    <xsl:template match="view:*">
        <xsl:call-template name="Error">
            <xsl:with-param name="Message">
                Unsupported spun tag: <xsl:value-of select="local-name(.)"/>
            </xsl:with-param>
        </xsl:call-template>
    </xsl:template>

    <!-- Bindings -->

    <xsl:template match="*" mode="Bind">
        <xsl:apply-templates select="*" mode="Bind" />
    </xsl:template>

    <xsl:template match="view:*" mode="Bind">
        <xsl:variable name="Id" select="generate-id(.)" />

        <xsl:text>/* </xsl:text>
        <xsl:value-of select="$Id"/>
        <xsl:text> */
        </xsl:text>

        <xsl:call-template name="LookupElement">
            <xsl:with-param name="Id" select="$Id"/>
        </xsl:call-template>

        <xsl:apply-templates select="." mode="BindView">
            <xsl:with-param name="Id" select="$Id" />
            <xsl:with-param name="Path">
                <xsl:text>'</xsl:text>
                <xsl:value-of select="@path"/>
                <xsl:text>'</xsl:text>
            </xsl:with-param>
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="view:inline" mode="BindView">
        <xsl:param name="Id" />
        <xsl:param name="Path" />

        <xsl:text>Bind(</xsl:text>
        <xsl:value-of select="$Id"/>
        <xsl:text>, </xsl:text>
        <xsl:value-of select="$Path"/>
        <xsl:text>, scalarUpdate);
        </xsl:text>
    </xsl:template>

    <xsl:template name="ConstructControl">
        <xsl:param name="Id" />
        <xsl:param name="Path" />
        <xsl:param name="ControlClass" />

        <xsl:text>new </xsl:text>
        <xsl:value-of select="$ControlClass"/>
        <xsl:text>(</xsl:text>
        <xsl:value-of select="$Id"/>
        <xsl:text>, </xsl:text>
        <xsl:value-of select="$Path"/>
        <xsl:text>);
        </xsl:text>
    </xsl:template>

    <xsl:template match="view:text" mode="BindView">
        <xsl:param name="Id" />
        <xsl:param name="Path" />

        <xsl:call-template name="ConstructControl">
            <xsl:with-param name="Id" select="$Id" />
            <xsl:with-param name="Path" select="$Path" />
            <xsl:with-param name="ControlClass" select="'TextField'" />
        </xsl:call-template>
    </xsl:template>

    <xsl:template match="view:checkbox" mode="BindView">
        <xsl:param name="Id" />
        <xsl:param name="Path" />

        <xsl:call-template name="ConstructControl">
            <xsl:with-param name="Id" select="$Id" />
            <xsl:with-param name="Path" select="$Path" />
            <xsl:with-param name="ControlClass" select="'CheckBox'" />
        </xsl:call-template>
    </xsl:template>

    <xsl:template match="view:int" mode="BindView">
        <xsl:param name="Id" />
        <xsl:param name="Path" />

        <xsl:call-template name="ConstructControl">
            <xsl:with-param name="Id" select="$Id" />
            <xsl:with-param name="Path" select="$Path" />
            <xsl:with-param name="ControlClass" select="'IntField'" />
        </xsl:call-template>
    </xsl:template>

    <xsl:template match="view:float" mode="BindView">
        <xsl:param name="Id" />
        <xsl:param name="Path" />

        <xsl:call-template name="ConstructControl">
            <xsl:with-param name="Id" select="$Id" />
            <xsl:with-param name="Path" select="$Path" />
            <xsl:with-param name="ControlClass" select="'FloatField'" />
        </xsl:call-template>
    </xsl:template>

    <xsl:template match="view:checkbox" mode="BindView">
        <xsl:param name="Id" />
        <xsl:param name="Path" />

        <xsl:call-template name="ConstructControl">
            <xsl:with-param name="Id" select="$Id" />
            <xsl:with-param name="Path" select="$Path" />
            <xsl:with-param name="ControlClass" select="'CheckBox'" />
        </xsl:call-template>
    </xsl:template>

    <xsl:template match="view:sequence" mode="BindView">
        <xsl:param name="Id" />
        <xsl:param name="Path" />

        <xsl:variable name="Binder" select="concat('Binder_', $Id)" />
        <xsl:variable name="PositionVariable" select="concat('Position_', $Id)" />
        <xsl:variable name="InstanceVariable" select="concat('Instance_', $Id)" />

        <xsl:text>var </xsl:text>
        <xsl:value-of select="$Binder"/>
        <xsl:text> = Class.create(SequenceBinder, {
            createElement: function(</xsl:text>
        <xsl:value-of select="$PositionVariable"/>
        <xsl:text>) {
        </xsl:text>

        <xsl:for-each select="*">
            <xsl:apply-templates select="*" mode="BindSequence">
                <xsl:with-param name="ParentId" select="$Id" />
                <xsl:with-param name="PathRoot" select="$Path" />
                <xsl:with-param name="PositionVariable" select="$PositionVariable" />
            </xsl:apply-templates>
        </xsl:for-each>

        <xsl:value-of select="'    '"/>
        <xsl:text>}
        </xsl:text>

        <xsl:text>});
        </xsl:text>

        <xsl:text>var </xsl:text>
        <xsl:value-of select="$InstanceVariable"/>
        <xsl:text> = new </xsl:text>
        <xsl:value-of select="$Binder"/>
        <xsl:text>();
        </xsl:text>

        <xsl:variable name="UpdateFunction">
            <xsl:text>function(id, v) { </xsl:text>
            <xsl:value-of select="$InstanceVariable"/>
            <xsl:text>.update(id, v); }</xsl:text>
        </xsl:variable>

        <xsl:text>Bind(</xsl:text>
        <xsl:value-of select="$Id"/>
        <xsl:text>, </xsl:text>
        <xsl:value-of select="$Path"/>
        <xsl:text> + '.Count', </xsl:text>
        <xsl:value-of select="$UpdateFunction"/>
        <xsl:text>);
        </xsl:text>
    </xsl:template>

    <xsl:template match="view:*" mode="BindView" />

    <xsl:template name="LookupElement">
        <xsl:param name="Id" />

        <xsl:text>var </xsl:text>
        <xsl:value-of select="$Id"/>
        <xsl:text> = $('</xsl:text>
        <xsl:value-of select="$Id"/>
        <xsl:text>');
        </xsl:text>
    </xsl:template>

    <!-- Sequence Binding -->

    <xsl:template match="html:*" mode="BindSequence">
        <xsl:param name="ParentId" />
        <xsl:param name="PathRoot" />
        <xsl:param name="PositionVariable" />

        <xsl:variable name="Id" select="generate-id(.)"/>

        <xsl:call-template name="BindHtmlNodeToSequence">
            <xsl:with-param name="ParentId" select="$ParentId" />
            <xsl:with-param name="Id" select="$Id" />
            <xsl:with-param name="Argument" select="name()" />
            <xsl:with-param name="Method" select="'createElement'" />
        </xsl:call-template>

        <xsl:apply-templates select="child::node()" mode="BindSequence">
            <xsl:with-param name="ParentId" select="$Id" />
            <xsl:with-param name="PathRoot" select="$PathRoot" />
            <xsl:with-param name="PositionVariable" select="$PositionVariable"/>
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="text()" mode="BindSequence">
        <xsl:param name="ParentId" />

        <xsl:call-template name="BindHtmlNodeToSequence">
            <xsl:with-param name="ParentId" select="$ParentId" />
            <xsl:with-param name="Id" select="generate-id(.)" />
            <xsl:with-param name="Argument" select="normalize-space(.)" />
            <xsl:with-param name="Method" select="'createTextNode'" />
        </xsl:call-template>
    </xsl:template>

    <xsl:template match="view:inline" mode="BindSequence">
        <xsl:param name="ParentId" />
        <xsl:param name="PathRoot" />
        <xsl:param name="PositionVariable" />

        <xsl:call-template name="BindViewNodeToSequence">
            <xsl:with-param name="ParentId" select="$ParentId" />
            <xsl:with-param name="ElementName" select="'span'" />
            <xsl:with-param name="PathRoot" select="$PathRoot" />
            <xsl:with-param name="PositionVariable" select="$PositionVariable"/>
        </xsl:call-template>
    </xsl:template>

    <xsl:template match="view:checkbox" mode="BindSequence">
        <xsl:param name="ParentId" />
        <xsl:param name="PathRoot" />
        <xsl:param name="PositionVariable" />

        <xsl:call-template name="BindControlNodeToSequence">
            <xsl:with-param name="ParentId" select="$ParentId" />
            <xsl:with-param name="ElementName" select="'input'" />
            <xsl:with-param name="PathRoot" select="$PathRoot" />
            <xsl:with-param name="PositionVariable" select="$PositionVariable"/>
            <xsl:with-param name="Type" select="'checkbox'" />
        </xsl:call-template>
    </xsl:template>

    <xsl:template match="view:text" mode="BindSequence">
        <xsl:param name="ParentId" />
        <xsl:param name="PathRoot" />
        <xsl:param name="PositionVariable" />

        <xsl:call-template name="BindControlNodeToSequence">
            <xsl:with-param name="ParentId" select="$ParentId" />
            <xsl:with-param name="ElementName" select="'input'" />
            <xsl:with-param name="PathRoot" select="$PathRoot" />
            <xsl:with-param name="PositionVariable" select="$PositionVariable"/>
            <xsl:with-param name="Type" select="'text'" />
        </xsl:call-template>
    </xsl:template>

    <xsl:template name="BindHtmlNodeToSequence">
        <xsl:param name="ParentId" />
        <xsl:param name="Method" />
        <xsl:param name="Argument" />

        <xsl:variable name="Id" select="generate-id(.)"/>

        <xsl:value-of select="'        '"/>
        <xsl:text>var </xsl:text>
        <xsl:value-of select="$Id"/>
        <xsl:text> = document.</xsl:text>
        <xsl:value-of select="$Method"/>
        <xsl:text>('</xsl:text>
        <xsl:value-of select="$Argument"/>
        <xsl:text>');
        </xsl:text>

        <xsl:value-of select="'        '"/>
        <xsl:value-of select="$ParentId"/>
        <xsl:text>.appendChild(</xsl:text>
        <xsl:value-of select="$Id"/>
        <xsl:text>);
        </xsl:text>
    </xsl:template>

    <xsl:template name="BindViewNodeToSequence">
        <xsl:param name="ParentId" />
        <xsl:param name="ElementName" />
        <xsl:param name="PathRoot" />
        <xsl:param name="PositionVariable" />
        <xsl:param name="SetupStep" />

        <xsl:variable name="Id" select="generate-id(.)"/>

        <xsl:variable name="PathExpression">
            <xsl:value-of select="$PathRoot"/>
            <xsl:text> + '[' + </xsl:text>
            <xsl:value-of select="$PositionVariable"/>
            <xsl:text> + ']</xsl:text>
            <xsl:value-of select="@path"/>
            <xsl:text>'</xsl:text>
        </xsl:variable>

        <xsl:value-of select="'        '"/>
        <xsl:text>var </xsl:text>
        <xsl:value-of select="$Id"/>
        <xsl:text> = document.createElement('</xsl:text>
        <xsl:value-of select="$ElementName"/>
        <xsl:text>');
        </xsl:text>

        <xsl:value-of select="$SetupStep"/>

        <xsl:value-of select="'        '"/>
        <xsl:value-of select="$ParentId"/>
        <xsl:text>.appendChild(</xsl:text>
        <xsl:value-of select="$Id"/>
        <xsl:text>);
        </xsl:text>

        <xsl:apply-templates select="." mode="BindView">
            <xsl:with-param name="Id" select="$Id" />
            <xsl:with-param name="Path" select="$PathExpression" />
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template name="BindControlNodeToSequence">
        <xsl:param name="ParentId" />
        <xsl:param name="ElementName" />
        <xsl:param name="PathRoot" />
        <xsl:param name="PositionVariable" />
        <xsl:param name="Type" />

        <xsl:variable name="Var" select="generate-id(.)" />

        <xsl:variable name="Id">
            <xsl:text>'</xsl:text>
            <xsl:value-of select="$Var"/>
            <xsl:text>_' + </xsl:text>
            <xsl:value-of select="$PositionVariable"/>
        </xsl:variable>

        <xsl:variable name="SetupStep">
            <xsl:value-of select="'        '"/>
            <xsl:value-of select="$Var"/>
            <xsl:text>.setAttribute('type', '</xsl:text>
            <xsl:value-of select="$Type"/>
            <xsl:text>');
        </xsl:text>

            <xsl:value-of select="'        '"/>
            <xsl:value-of select="$Var"/>
            <xsl:text>.setAttribute('id', </xsl:text>
            <xsl:value-of select="$Id"/>
            <xsl:text>);
        </xsl:text>
        </xsl:variable>

        <xsl:call-template name="BindViewNodeToSequence">
            <xsl:with-param name="ParentId" select="$ParentId"/>
            <xsl:with-param name="ElementName" select="$ElementName"/>
            <xsl:with-param name="PathRoot" select="$PathRoot"/>
            <xsl:with-param name="PositionVariable" select="$PositionVariable"/>
            <xsl:with-param name="SetupStep" select="$SetupStep"/>
        </xsl:call-template>

        <xsl:if test="@label">
            <xsl:call-template name="BindLabelNodeToSequence">
                <xsl:with-param name="ParentId" select="$ParentId"/>
                <xsl:with-param name="For" select="$Id"/>
                <xsl:with-param name="Text" select="@label"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>

    <xsl:template name="BindLabelNodeToSequence">
        <xsl:param name="ParentId" />
        <xsl:param name="For" />
        <xsl:param name="Text" />

        <xsl:variable name="LabelVar">
            <xsl:text>LabelFor_</xsl:text>
            <xsl:value-of select="generate-id(.)" />
        </xsl:variable>

        <xsl:value-of select="'        '"/>
        <xsl:text>var </xsl:text>
        <xsl:value-of select="$LabelVar"/>
        <xsl:text> = document.createElement('label');
        </xsl:text>

        <xsl:value-of select="'        '"/>
        <xsl:value-of select="$LabelVar"/>
        <xsl:text>.setAttribute('for', </xsl:text>
        <xsl:value-of select="$For"/>
        <xsl:text>);
        </xsl:text>

        <xsl:value-of select="'        '"/>
        <xsl:value-of select="$ParentId"/>
        <xsl:text>.appendChild(</xsl:text>
        <xsl:value-of select="$LabelVar"/>
        <xsl:text>);
        </xsl:text>

        <xsl:value-of select="'        '"/>
        <xsl:value-of select="$LabelVar"/>
        <xsl:text>.appendChild(document.createTextNode('</xsl:text>
        <xsl:value-of select="$Text"/>
        <xsl:text>'));
        </xsl:text>
    </xsl:template>
</xsl:stylesheet>
